/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package CastleDefense;

import CastleDefense.Enemy.MonsterType;
import CastleDefense.Managers.GameManager;
import CastleDefense.units.PlacedUnit;
import CastleDefense.units.PlacedUnit.StateEffect;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

/**
 *
 * @author Firedemon
 */
public class Projectile
{
    private Sprite      sprite;
    private Enemy       target;
    private PlacedUnit  parent;
    private double      damage;
    private double      speed;
    private double      range;
    private double      x,y,angle;

    private String      damageType;
    
    private boolean     killUnit = false;
    private boolean     rotate   = false;
    
    private int[] effects;

    private ArrayList<StateEffect> projEffects = new ArrayList<StateEffect>();

    /** The rectangle used for this enemy during collisions  resolution */
    private Rectangle me  = new Rectangle();
    /** The rectangle used for other entities during collision resolution */
    private Rectangle him = new Rectangle();

    /**
     * 
     * @param speed The speed of the projectile.
     * @param range The max range of the projectile.
     * @param damage The damage of the projectile.
     * @param ammo StateEffect The type of projectile.
     * @param ref String reference of projectile Sprite location.
     * @param x Starting projectile x position.
     * @param y Starting projectile y position.
     * @param target The projectiles target to hit.
     * @param parent The projectiles parent, who shot it.
     */
    public Projectile(double speed, double range, double damage, int[] projEffect,
                      String ref, double x, double y, Enemy target, PlacedUnit parent)
    {
        this.x          = x;
        this.y          = y;
        this.effects    = projEffect;
        this.speed      = speed;
        this.range      = range;
        this.damage     = damage;
        this.target     = target;
        this.sprite     = SpriteStore.get().getSprite(GameManager.getInstance().PROJECTILES_PATH()+ref);
        this.parent     = parent;
        this.damageType = parent.getUnitClass();

        setEffects();
    }

    /**
     * 
     * @param speed The speed of the projectile.
     * @param ref String reference of projectile Sprite location.
     * @param x Starting projectile x position.
     * @param y Starting projectile y position.
     * @param parent The projectiles parent, who shot it.
     * @param target The projectiles target to hit.
     */
    public Projectile(double speed, String ref, double x, double y, Enemy parent, PlacedUnit target)
    {
        this.x         = x;
        this.y         = y;
        this.speed     = speed;
        this.target    = parent;
        this.sprite    = SpriteStore.get().getSprite(GameManager.getInstance().PROJECTILES_PATH()+ref);
        this.parent    = target;
        this.killUnit  = true;
    }
    
    public void moveToTarget()
    {
        if(killUnit)
        {
            moveToUnit();
            return;
        }
        
        if(target == null || target.isDead())
            Game.removeProjectile(this);

        //projectile hits enemy
        if(this.collidesWithEnemy(target) && this.damagesTarget())
        {
        	if(rotate)
        		rotate = false;
        	
            target.damageCalculation(this);
        }

        double dx, dy, ySpeed, xSpeed, dist;

        dy     = target.getMidY() - this.y-16;       //how far proj is from the enemy (x)
        dx     = target.getMidX() - this.x;          //how far proj is from the enemy (y)
        dist   = Math.sqrt(dx*dx + dy*dy);           //total distance to target
        angle  = Math.atan2(dy,dx);                  //the angle that it's going to move
        
        //add more speed to projectile if the enemy is very fast
        if(target.getSpeed()>10)
        {
            ySpeed = Math.sin(angle) * (this.speed+(target.getSpeed()/5));       //calculate how much it should move towards the enemy vertically
            xSpeed = Math.cos(angle) * (this.speed+(target.getSpeed()/5));       //calculate how much it should move towards the enemy horizontally
        }
        else
        {
            ySpeed = Math.sin(angle) * this.speed;       //calculate how much it should move towards the enemy vertically
            xSpeed = Math.cos(angle) * this.speed;       //calculate how much it should move towards the enemy horizontally
        }
        
        //move the bullet towards the enemy
        if(dist <= this.getRange())
        {
            this.x += xSpeed;
            this.y += ySpeed;
        }
        else
        {
            Game.removeProjectile(this);
            return;
        }
        
        if(angle % 2 == 0)
        	if(rotate)
        		rotate = false;
    }

    public void moveToUnit()
    {
        if(parent == null)
            Game.removeProjectile(this);
        
        //projectile hits & kills target
        if(this.collidesWithUnit(parent))
        {
        	
        	
            Game.removeUnit(parent);
            Game.removeProjectile(this);
        }
        
        double dx, dy, ySpeed, xSpeed;

        dy     = parent.getMidY() - this.y;         //how far proj is from the enemy (x)
        dx     = parent.getMidX() - this.x;         //how far proj is from the enemy (y)
        //dist   = Math.sqrt(dx*dx + dy*dy);        //total distance to target
        angle  = Math.atan2(dy,dx); 
                
        ySpeed = Math.sin(angle) * this.speed;      //calculate how much it should move towards the enemy vertically
        xSpeed = Math.cos(angle) * this.speed;      //calculate how much it should move towards the enemy horizontally
        
        if(angle % 2 == 0)
        	if(rotate)
        		rotate = false;
        
        this.x += xSpeed;
        this.y += ySpeed;
    }
    
    private boolean collidesWithEnemy(Enemy e)
    {
        me.setBounds((int) x, (int) y, sprite.getWidth(), sprite.getHeight());
        him.setBounds((int) e.getX(), (int) e.getY(), e.getSprite().getWidth(), e.getSprite().getHeight());

        return me.intersects(him);
    }
    
    private boolean collidesWithUnit(PlacedUnit u)
    {
        me.setBounds((int) x, (int) y, sprite.getWidth(), sprite.getHeight());
        him.setBounds((int) u.getX(), (int) u.getY(), u.getSprite().getWidth(null), u.getSprite().getHeight(null));

        return me.intersects(him);
    }

    private boolean damagesTarget()
    {
        String unitClass = this.getParent().getUnitClass();
        if(target.getMobType() == MonsterType.FLYING && unitClass.equals("Traps"))
            return false;
        
        if(target.getMobType() == MonsterType.GHOST && !unitClass.equals("Magic"))
            return false;
        
        if(target.getMobType() == MonsterType.MAGIC_IMMUNE && unitClass.equals("Magic"))
            return false;

        return true;
    }

    public void draw(Graphics g)
    {
    	if(!rotate)
    	{
	        Image projImage = this.getImage();
	
	        int newSize = (int) (Math.sqrt((Math.pow(projImage.getWidth(null), 2) + Math.pow(projImage.getHeight(null), 2))));
	        
	        BufferedImage bufferedImage = new BufferedImage(newSize, newSize, BufferedImage.TYPE_INT_ARGB);
	        BufferedImage newImage      = new BufferedImage(newSize, newSize, BufferedImage.TYPE_INT_ARGB);;
	        
	        Graphics gb = bufferedImage.getGraphics();
	        gb.drawImage(projImage, 0, 0, null);
	        gb.dispose();
	        
	        AffineTransform transformer = new AffineTransform();
	        transformer.rotate(angle, projImage.getWidth(null)/2, projImage.getHeight(null)/2);
	        AffineTransformOp op = new AffineTransformOp(transformer, AffineTransformOp.TYPE_BILINEAR);
	        newImage = op.filter(bufferedImage, newImage);
	
	        this.setSprite(new Sprite(Toolkit.getDefaultToolkit().createImage(newImage.getSource())));
	        
	        rotate = true;
    	}
    	
        sprite.draw(g, (int)x, (int)y);
    }

    public PlacedUnit getParent()
    {
        return parent;
    }

    public Sprite getSprite()
    {
        return sprite;
    }

    public void setSprite(Sprite spr)
    {
        this.sprite = spr;
    }

    public Enemy getTarget()
    {
        return target;
    }
    
    public double getDamage()
    {
        return damage;
    }

    public void setDamage(double dmg)
    {
        this.damage = dmg;
    }

    public String getDamageType()
    {
        return damageType;
    }
    
    public double getSpeed()
    {
        return speed;
    }

    public double getRange()
    {
        return range;
    }

    private void setEffects()
    {     
        for(int effect : effects)
        {
            projEffects.add(getEffect(effect));
        }
    }

    public ArrayList<StateEffect> getEffects()
    {
        return projEffects;
    }

    private StateEffect getEffect(int effect)
    {
        switch(effect)
        {
            case 0:
                return StateEffect.NONE;
            case 1:
                return StateEffect.SLOW;
            case 2:
                return StateEffect.SPLASH;
            case 3:
                return StateEffect.SNARE;
            case 4:
                return StateEffect.POISON;
            default:
                return StateEffect.NONE;
        }
    }

    public Image getImage()
    {
        return sprite.getImage();
    }

    public double getX()
    {
        return x;
    }

    public double getY()
    {
        return y;
    }
}
